import datetime
import json
import os
# redis
import uuid

from flask_redis import FlaskRedis

from db import Base, db

redis_client = FlaskRedis()

# token 过期时间  # 2小时
TOKEN_EXPIRE_TIMEOUT = int(os.getenv("TOKEN_EXPIRE_TIMEOUT", 60 * 60 * 2))
# token-key
TOKEN_KEY = os.getenv("TOKEN_KEY", "token")
# token 前辍
TOKEN_KEY_PREFIX = os.getenv("TOKEN_KEY", "flaskrbac:")


class AbstractToken:

    def set(self, data):
        pass

    def get(self, token):
        pass

    def remove(self, token):
        pass


class TokenFactory:
    @staticmethod
    def get_instance():
        token_strategy = os.getenv("TOKEN_STRATEGY")
        if token_strategy == 'redis':
            return RedisToken()
        elif token_strategy == 'mysql':
            return MysqlToken()
        else:
            return RedisToken()

    @staticmethod
    def create_token(data):

        uid = str(uuid.uuid4())
        suid = ''.join(uid.split('-'))
        return suid


class RedisToken(AbstractToken):

    def set(self, data):
        token = data.get('token')
        token = TOKEN_KEY_PREFIX + token
        p = redis_client.pipeline()
        p.set(token, json.dumps(data, ensure_ascii=False))
        p.expire(token, TOKEN_EXPIRE_TIMEOUT)
        p.execute()

    def get(self, token):
        data = redis_client.get(TOKEN_KEY_PREFIX + token)
        if data is None:
            return None
        p = redis_client.pipeline()
        p.expire(TOKEN_KEY_PREFIX + token, TOKEN_EXPIRE_TIMEOUT)
        p.execute()
        return json.loads(data.decode("utf-8"))

    def remove(self, token):
        p = redis_client.pipeline()
        p.delete(TOKEN_KEY_PREFIX + token)
        p.execute()


class AccessToken(Base):
    __tablename__ = "access_token"
    id = db.Column(db.Integer, primary_key=True, comment="主键")
    userId = db.Column(db.Integer, name="user_id", unique=False, nullable=False, comment="用户id")
    token = db.Column(db.String(40), name="token", unique=True, nullable=False, comment="token")


class MysqlToken(AbstractToken):
    def set(self, data):
        accessToken = AccessToken()
        accessToken.token = data.get("token")
        accessToken.userId = data.get("userId")
        db.session.add(accessToken)
        db.session.commit()

    def get(self, token):
        now = datetime.datetime.now()
        delta = datetime.timedelta(seconds=TOKEN_EXPIRE_TIMEOUT)
        expireTime = now - delta
        accessToken = AccessToken.query.filter_by(token=token).filter(AccessToken.updateTime > expireTime).first()
        if accessToken is None:
            return None
        else:
            # 更新时间,基类已经做了自动更新更新时间，这里只修改一个字段触发更新操作即可
            AccessToken.query.filter_by(id=accessToken.id).update({
                AccessToken.isDeleted: accessToken.isDeleted
            })
            # 提交事务
            db.session.commit()
            return AccessToken.to_dict(accessToken)

    def remove(self, token):
        AccessToken.query.filter_by(token=token).delete()
